package tarantula.reporter;

import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import tarantula.collector.ExecResults;
import tarantula.instrumenter.SourcePos;
import tarantula.util.FileUtils;
import tarantula.util.Globals;

/**
 * Generating a clear report for Tarantula
 * */
public class ReportGenerator {
	
	public final Map<SourcePos, ExecResults> retSources;
	public final String outputFilePath;
	
	//an optional setting, the source folder
	public String sourceDir = null;
	
	public boolean remove_repeat = false;
	
	public ReportGenerator(Map<SourcePos, ExecResults> retSources, String outputFilePath) {
		assert retSources != null;
		assert outputFilePath != null;
		if(!(retSources instanceof LinkedHashMap)) {
			throw new RuntimeException("It must be a linked hash map to keep order.");
		}
		this.retSources = retSources;
		this.outputFilePath = outputFilePath;
	}
	
	public ReportGenerator(Map<SourcePos, ExecResults> retSources, String outputFilePath, String sourceDir) {
		this(retSources, outputFilePath);
		assert sourceDir != null;
		FileUtils.checkDirExist(sourceDir);
		this.sourceDir = sourceDir;
	}
	
	public void generateReport() {
		int totalcount = 0;
		Map<String, Set<Integer>> cachedAddedStmt = new HashMap<String, Set<Integer>>();
		
		StringBuilder sb = new StringBuilder();
		
		sb.append("The Tarantula fault diagnosis results: ");
		sb.append(Globals.lineSep);
		sb.append("---------------------------------------");
		sb.append(Globals.lineSep);
		
		for(SourcePos pos : retSources.keySet()) {
			if(!cachedAddedStmt.containsKey(pos.fileName)) {
				cachedAddedStmt.put(pos.fileName, new HashSet<Integer>());
			}
			Set<Integer> lineIndices = cachedAddedStmt.get(pos.fileName);
			if(lineIndices.contains(pos.lineNum) && this.remove_repeat && pos.lineNum!=-1/*aware of this*/) {
				continue;
			} else {
			  lineIndices.add(pos.lineNum);
			}
			
			ExecResults result = retSources.get(pos);
			sb.append(pos.fileName + ",  line: " + pos.lineNum + ", code: " +getSourceCode(pos)
					+ ",  results: " + result.toString());
			sb.append(Globals.lineSep);
			totalcount++;
		}
		
		sb.append("---------------------------------------");
		sb.append(Globals.lineSep);
		sb.append("Num of suspicious statement: " + totalcount);
		
		FileUtils.writeToFileNoExp(sb.toString(), outputFilePath);
	}
	
	private Map<String, List<String>> cachedFileContent = new HashMap<String, List<String>>();
	
	private String getSourceCode(SourcePos pos) {
		//no source dir or the line num is unknown
		if(this.sourceDir == null || pos.lineNum == -1) {
			return pos.jimpleCode;
		}
		String fileName = pos.fileName;
		if(fileName.indexOf("$") != -1) {
			fileName = fileName.substring(0, fileName.indexOf("$"));
		}
		fileName = fileName.replace(".", "/") + ".java";
		if(!cachedFileContent.containsKey(fileName)) {
		    File f = new File(this.sourceDir + Globals.fileSep + fileName);
		    if(!f.exists()) {
		    	throw new RuntimeException("File corresponding to: " + fileName
		    			+ ", with original recorded: " + pos.fileName + " does not exist.");
		    }
		    List<String> content = FileUtils.readWholeNoExp(f.getAbsolutePath());
		    cachedFileContent.put(fileName, content);
		}
		
		List<String> fileContent = cachedFileContent.get(fileName);
		if(fileContent.size() < pos.lineNum) {
			throw new RuntimeException("The file: " + fileName + " only contains: " + fileContent.size()
					+ " lines, which is less than the required num: " + pos.lineNum);
		}
		
		return fileContent.get(pos.lineNum);
	}
}